const TestbedFlow = require('./mongo/testbed-flow');
const utility = require('./utility');

async function deleteFlow(fileId, { ruleType, functionMangledId, functionId }) {
  if (!fileId) {
    return;
  }
  const filter = { fileId };
  if (ruleType) {
    ruleType = utility.isArrayType(ruleType) ? ruleType : [].concat(ruleType);
    filter.ruleType = { $in: ruleType };
  }
  if (functionMangledId) {
    functionMangledId = utility.isArrayType(functionMangledId) ? functionMangledId : [].concat(functionMangledId);
    filter.functionMangledId = { $in: functionMangledId };
  }
  if (functionId) {
    filter.functionId = utility.isArrayType(functionId) ? functionId : [].concat(functionId);
    filter.functionId = { $in: functionId };
  }
  await TestbedFlow.deleteMany(filter);
}
async function handlePathTrackerMessage(pathTrackerData, {
  fileId, filePathMap, funcMangledIdMap, projectVersion,
}) {
  const newPathTrackerData = [];
  const { creatorId, projectId, _id: versionId } = projectVersion;
  Object.keys(pathTrackerData).forEach((mangledId) => {
    if (!funcMangledIdMap.has(mangledId)) {
      return;
    }
    const func = pathTrackerData[mangledId];
    Object.keys(func).forEach((variableName) => {
      const newPaths = [];
      const { paths } = func[variableName];
      if (paths) {
        paths.forEach((chilPath) => {
          const newPath = [];
          chilPath.forEach((item) => {
            if (!item.file || !item.range || !filePathMap.has(item.file)) {
              return;
            }
            newPath.push({
              fileId: filePathMap.get(item.file),
              range: item.range,
            });
          });
          if (newPath.length > 0) {
            newPaths.push(newPath);
          }
        });
      }
      newPathTrackerData.push({
        creatorId,
        projectId,
        versionId,
        fileId,
        functionId: funcMangledIdMap.get(mangledId),
        functionMangledId: mangledId,
        ruleType: 'PathTracker',
        data: {
          variableName,
          paths: newPaths,
        },
      });
    });
  });
  if (newPathTrackerData.length === 0) {
    return;
  }
  await TestbedFlow.insertMany(newPathTrackerData);
}
async function handleDependencyAnalyzerMessage(dependencyAnalyzerData, { fileId, funcMangledIdMap, projectVersion }) {
  const newDependencyAnalyzerData = [];
  const { creatorId, projectId, _id: versionId } = projectVersion;
  Object.keys(dependencyAnalyzerData).forEach((mangledId) => {
    if (!funcMangledIdMap.has(mangledId)) {
      return;
    }
    newDependencyAnalyzerData.push({
      creatorId,
      projectId,
      versionId,
      fileId,
      functionId: funcMangledIdMap.get(mangledId),
      functionMangledId: mangledId,
      ruleType: 'DependencyAnalyzer',
      data: dependencyAnalyzerData[mangledId],
    });
  });
  if (newDependencyAnalyzerData.length === 0) {
    return;
  }
  await TestbedFlow.insertMany(newDependencyAnalyzerData);
}
async function handleStatisticsMessage(statisticsData, { fileId, funcMangledIdMap, projectVersion }) {
  const newStatisticsData = [];
  const { creatorId, projectId, _id: versionId } = projectVersion;
  Object.keys(statisticsData).forEach((mangledId) => {
    if (!funcMangledIdMap.has(mangledId)) {
      return;
    }
    newStatisticsData.push({
      creatorId,
      projectId,
      versionId,
      fileId,
      functionId: funcMangledIdMap.get(mangledId),
      functionMangledId: mangledId,
      ruleType: 'Statistics',
      data: statisticsData[mangledId],
    });
  });
  if (newStatisticsData.length === 0) {
    return;
  }
  await TestbedFlow.insertMany(newStatisticsData);
}
async function handleDateObjectAnalyzerMessage(dateObjectAnalyzerData, { fileId, projectVersion }) {
  const newDateObjectAnalyzerData = [];
  const { creatorId, projectId, _id: versionId } = projectVersion;
  Object.keys(dateObjectAnalyzerData).forEach((objectName) => {
    const objectData = dateObjectAnalyzerData[objectName];
    const attributes = objectData.attributes || {};
    const newAttrs = {};
    Object.keys(attributes).forEach((attrKey) => {
      newAttrs[attrKey] = {};
      newAttrs[attrKey].attrType = attributes[attrKey].type;
      newAttrs[attrKey].attrValue = attributes[attrKey].value;
      newAttrs[attrKey].attrVisibility = attributes[attrKey].visibility;
    });
    newDateObjectAnalyzerData.push({
      creatorId,
      projectId,
      versionId,
      fileId,
      functionId: '',
      functionMangledId: '',
      ruleType: 'DateObjectAnalyzer',
      data: {
        objectName,
        attributes: newAttrs,
        kind: objectData.kind,
      },
    });
  });
  if (newDateObjectAnalyzerData.length === 0) {
    return;
  }
  await TestbedFlow.insertMany(newDateObjectAnalyzerData);
}
async function handleLifetimeAnalyzerMessage(lifetimeAnalyzerData, {
  fileId, filePathMap, funcMangledIdMap, projectVersion,
}) {
  const newLifetimeAnalyzerData = [];
  lifetimeAnalyzerData.forEach((item) => {
    if (!filePathMap.has(item.file) || !item.lifetime) {
      return;
    }
    if (item.scope === 'function' && !funcMangledIdMap.has(item.function)) {
      return;
    }
    const { creatorId, projectId, _id: versionId } = projectVersion;
    const obj = {
      creatorId,
      projectId,
      versionId,
      fileId,
      ruleType: 'LifetimeAnalyzer',
      data: {
        variableName: item.name,
        scope: item.scope,
        storage: item.storage,
        lifetime: item.lifetime,
        fileId: filePathMap.get(item.file),
      },
    };
    if (item.function) {
      obj.functionId = funcMangledIdMap.get(item.function);
      obj.functionMangledId = item.function;
    }
    newLifetimeAnalyzerData.push(obj);
  });
  if (newLifetimeAnalyzerData.length === 0) {
    return;
  }
  await TestbedFlow.insertMany(newLifetimeAnalyzerData);
}
async function getTestbedFlow({ fileId, ruleType, functionId }) {
  const filter = { };
  if (fileId) {
    if (!utility.isArrayType(fileId)) {
      fileId = [].concat(fileId);
    }
    filter.fileId = { $in: fileId };
  }
  if (ruleType) {
    if (!utility.isArrayType(ruleType)) {
      ruleType = [].concat(ruleType);
    }
    filter.ruleType = { $in: ruleType };
  }
  if (functionId) {
    if (!utility.isArrayType(functionId)) {
      functionId = [].concat(functionId);
    }
    filter.functionId = { $in: functionId };
  }
  return TestbedFlow.find(filter);
}
module.exports = {
  deleteFlow,
  getTestbedFlow,
  handleStatisticsMessage,
  handlePathTrackerMessage,
  handleLifetimeAnalyzerMessage,
  handleDateObjectAnalyzerMessage,
  handleDependencyAnalyzerMessage,

};

exports = Object.assign(exports, module.exports);
